<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div id="app">
			<input type="text" v-model="msg"/>
			<h3>{{msg}}</h3>
		</div>
		<script type="text/javascript">
			
			function LcVue(options={}){
				this.$options = options; //将配置的参数放置到$options里
				this.$el = document.querySelector(options.el) //获取el元素
				this._data = options.data;
				this._watcherTpl = {} //watcher池
				// 传入数据对象，执行oberser函数，对data的属性进行重写
				this._observer(this._data)
				//传入元素$el，执行编译模板，进行发布订阅
				this._compile(this.$el)
			}
			
			// 实现数据监听器
			LcVue.prototype._observer = function(obj){
				
				Object.keys(obj).forEach((key)=>{
					// 遍历每一个属性
					this._watcherTpl[key] = {
						_directives:[]
					}
					
					let watcherTpl = this._watcherTpl[key];
					//获取属性值
					var value = obj[key];//获取属性值
					Object.defineProperty(this._data,key,{
						configurable:true,//设置删除
						enumerable:true,//可以遍历
						get(){
							console.log(`${key}获取的值是：${value}`);
							// 获取值的时候直接返回value
							return value;
						},
						set(newVal){
							//改变值，就会触发set函数
							// 判断新赋值的内容是否与原先不一样
							if(newVal!==value){
								value = newVal;
								// 将watcher订阅池里的相对应的属性进行触发更新
								watcherTpl._directives.forEach(item=>{
									item.update();
								})
							}
						}
					})
				})

			}
			
			function Watcher(el,vm,val,attr){
				this.el = el;
				this.vm = vm;
				this.val = val;
				this.attr = attr;
				this.update()
			}
			
			Watcher.prototype.update = function(){
				this.el[this.attr] = this.vm._data[this.val]
			}
			
			LcVue.prototype._compile = function(el){
				var nodes = el.children; //获取dom元素的子元素
				var len = nodes.length;
				for(var i = 0;i<len;i++){
					var node = nodes[i];
					if(node.children.length){
						// 递归深度遍历
						this._compile(node)
					}
					
					if(node.hasAttributes('v-model')&&node.tagName=="INPUT"){
						node.addEventListener('input',((key)=>{
							// 获取绑定v-model指令的值
							var attVal = node.getAttribute('v-model')
							this._watcherTpl[attVal]._directives.push(new Watcher(node,this,attVal,'value'))
							return ()=>{
								this._data[attVal] = nodes[key].value;
							}
						})(i))
					}
					
					var reg = /\{\{\s*(.*?)\s*\}\}/g;
					var txt = node.textContent;
					if(reg.test(txt)){
						node.textContent = txt.replace(reg,(matched,placeholder)=>{
							let attVal = placeholder;
							if(!this._watcherTpl[attVal]._directives){
								this._watcherTpl[attVal]._directives=[]
							}
							this._watcherTpl[attVal]._directives.push(new Watcher(node,this,placeholder,"innerHTML"))
							
							return placeholder.split('.').reduce((val,key)=>{
								return this._data[key];
							},this.$el)
							
						})
					}
				}
			}
			
		</script>
		
		
		<script type="text/javascript">
			var app = new LcVue({
				el:"#app",
				data:{
					msg:"helloworld"
				}
			})
			
		</script>
	</body>
</html>
